{"componentChunkName":"component---src-templates-article-js","path":"/article/komunikacja-frontend-backend-w-react-express-i-rest-api-moje-podejscie.","result":{"data":{"strapiArticles":{"title":"Komunikacja frontend-backend w React, Express i REST API - moje podejście.","content":"W świecie Javascriptu istnieje mnóstwo frameworków i bibliotek do budowania aplikacji webowych. Najpopularniejszą technologią na frontendzie jest React, a po stronie serwera - Express, Koa czy Nest.js. Podczas budowania aplikacji fullstackowej ważne jest, aby zaplanować dobrze architekturę, strukturę katalogów i sposów przepływu danych w obrębie naszej aplikacji. W tym artykule przedstawię moje podejścia do problemu jakim jest komunikacja między tymi dwoma światami z użyciem tradycyjnego REST API.\n\nJako przykład, chciałbym przedstawić kilka części mojej aplikacji z quizami którą tworzyłem przez ostatnie miesiące. Kliknij, by przejść do projektu [tutaj](https://github.com/krzysztof01-sz/squizer-app-frontend)\n\n#### Dashboard\n\nW komponencie dashboard mamy listę z quizami stworzonymi przez użytkowników. Aby odczytać informacje o tych quizach musimy wykonać zapytanie do naszego API. Właśnie tutaj zaczyna się cała komunikacja.\n\nPrzejdź do => /src/pages/Dashboard/index.js \n\n```\nconst Dashboard = () => {\n  const { data: quizzes, loading, error } = useFetching(getQuizzes);\n\n  if (loading) return <Loader />;\n  if (error) return <ErrorPage msg={error} />;\n\n  return (\n    <Layout>\n...\n```\n\n#### API na frontendzie\n\nW linii drugiej wywołuje własny hook o nazwie useFetching z argumentem getQuizzes. Ten argument to referencja do funkcji w pliku /src/api.index.js. Zobaczmy jak ta funkcja wygląda a następnie pokażę co dzieje się w hooku useFetching.\n\n```\nexport const getQuizzes = async () => {\n  const { data } = await instance.get('/api/quizzes').catch(({ response }) => response);\n  return data;\n};\n```\n\nJest to zwykła asynchroniczna funkcja, która wysyła zapytanie do ścieżki /api/quizzes używając paczki axios. Używam również metody catch aby wyłapać potencjalne błędy. Wszystkie informacje znajdują się we właściwości data, więc destrukturyzuje je używając składni ES6. \n\n#### Hook useFetching\nPrzejdź do => /src/hooks/useFetching.js \n\n\n```\nexport const useFetching = (method, ...methodArgs) => {\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState(null);\n  const [data, setData] = useState(undefined);\n\n  useEffect(() => {\n    const cancelToken = axios.CancelToken;\n    const source = cancelToken.source();\n\n    (async () => {\n      const { type, data, msg } = await method([...methodArgs]);\n\n      if (type === responseTypes.success) {\n        setData(data);\n      } else {\n        setError(msg);\n      }\n\n      setLoading(false);\n    })();\n\n    return () => source.cancel();\n  }, []);\n\n  return { data, loading, error };\n};\n```\n\nuseFetching to nic innego jak funkcja, która jako parametr przyjmuje referencję do funkcji, którą ma wywołać oraz tablicę opcjonalnych parametrów, które ta funkcja przyjmuje. Poniżej deklaruję stan (loading, error i data). W linii 10 wywołuję przekazaną jako parametr funkcję z argumentami używając spread operatora i wtedy desktrukturyzuję response, data i wiadomość z błędem (tutaj to wartość o nazwie msg). Potem, jeżeli zapytanie zakończyło się pomyślnie ustawiam zmienną data, error i wartość zmiennej loading na false aby dać znak, że żądanie się zakończyło, a komponent loader może zniknąć. Pod koniec zwracam obiekt z niezbędnymi danymi do wyświetlenia w komponencie. **Nie zapominaj by zakończyć wszyskie aktywne subskrypcje i zapytania asynchroniczne w funkcji czyszczącej komponent (cleanup function)**. Ja to robię używając accessTokena, który daje nam paczka axios. Teraz, wróćmy do naszego komponentu Dashboard.\n\n\n```\n  const { data: quizzes, loading, error } = useFetching(getQuizzes);\n```\n\nTutaj destrukturyzuję zwrócony obiekt z hooka, a następnie używam tych danych do zwizualizowania naszego stanu w UI.\n\n```\n  if (loading) return <Loader />;\n  if (error) return <ErrorPage msg={error} />;\n```\n\nI na końcu iterujemę po tablicy z quizami.\n\n#### Backend w Express\n\nPrzejdźmy teraz do backendu, aby zobaczyć w jaki sposób nasze zapytanie jest obsługiwane.\n\n[Backend tutaj, zapraszam](https://github.com/krzysztof01-sz/squizer-app-backend/ )\n\nPrzejdź do => /routes/api.js \n\n```\nrouter.get(\"/quizzes\", verify, quizzesController.getQuizzes);\n```\n\nW tej linii tworzę ścieżkę GET /quizzes która obsługuje zapytanie. Teraz zapytanie jest przekazywane do dedykowanego kontrolera.\n\n#### Kontroler\n\nRolą kontrolerów jest:\n\n<ul>\n<li>Przyjąć zapytanie</li>\n<li>Wywołać do pracy konkretny Service (jeżeli to konieczne)</li>\n<li>Zwrócić dane (lub błąd)</li>\n</ul>\n\nWłaśnie to dzieję się w pliku /controllers/quizzes.js.\n\n```\nmodule.exports.quizzesController = {\n  getQuizzes: async (req, res) => {\n    if (req.user) {\n      const { type, data, msg } = await QuizzesService.getQuizzes();\n\n      if (type === responseTypes.success) {\n        res.status(200).json({ type, data });\n      } else {\n        res.status(401).json({ type, msg });\n      }\n    }\n  },\n...\n```\n\nTutaj mamy metodę getQuizzes, która spełnia wyżej wymienione reguły:\n<ul>\n<li>Przyjmuje zapytanie (linia 2)</li>\n<li>Wywołuje odpowiedni Service (linia 4)</li>\n<li>Zwraca dane lub błąd w zależności od pola type (linie 6-10)</li>\n</ul>\nPierwsza i trzecia linia to kroki raczej trywialne. Najciekawsza rzecz dzieje się w linii 4, gdzie wywołujemy do pracy Service. \n\n#### Service\n\nRolą serwisów jest obsługa bardziej zaawansowanych i czasochłonnych zadań takich jak wykonywanie zapytań do bazy danych, wykonywanie obliczeń, czy przygotowywanie gotowego obiektu do odesłania kontrolerowi.\n\n```\nclass QuizzesService {\n  async getQuizzes() {\n    try {\n      const quizzes = await Quiz.find({}).sort({ creationDate: -1 });\n\n      if (quizzes.length > 0) {\n        return {\n          type: responseTypes.success,\n          data: quizzes,\n        };\n      } else throw messages.QUIZZES_NOT_FOUND;\n    } catch (e) {\n      return makeResponse(e, responseTypes.error);\n    }\n  }\n...\n```\n\nW ostatnim kroku, wykonuję zapytanie do MongoDB. Dostaję wszystkie quizy, i sortuję je według daty publikacji. Jeżeli quizy istnieją, zwraca je - w przeciwnym przypadku wyłapuję błąd i odsyłam odpowiedź.\n\nW tym miejscu polecam Ci używać składni try catch aby Twój kod był bardziej czytelny i łatwy do debugowania.\n\nVoila! Nasza komunikacja właśnie dobiegła końca. Teraz, dane zwrócone z Service'u odsyłamy kontrolerowi, a następinie na część frontendową. Następnie rozwiązywana zostaje funkcja w api, hooku useFetching i stamtąd trafia do odpowiedniego Reacowego komponentu, gdzie zaczęła się cała długa, ale fajna podróż! 😎 \n\nPodsumowując, tak wygląda komunikacja krok po kroku:\n\nKomponent Reactowy -> hook useFetching -> metoda w API -> ścieżka na backend -> kontroler -> Service (tutaj mamy nasze dane) -> kontroler -> metoda API -> hook useFetching -> komponent Reactowy (korzystamy z danych). \n\n ","date":"2021-06-02","minutesToRead":6,"slug":"komunikacja-frontend-backend-w-react-express-i-rest-api-moje-podejscie.","categories":[{"name":"frontend"},{"name":"backend"}]}},"pageContext":{"slug":"komunikacja-frontend-backend-w-react-express-i-rest-api-moje-podejscie."}},"staticQueryHashes":["3114346481"]}